/* Simple HTTP Server Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/

#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <esp_log.h>
#include <nvs_flash.h>
#include <sys/param.h>
#include "esp_netif.h"
#include "protocol_examples_common.h"
#include "protocol_examples_utils.h"
#include "esp_tls_crypto.h"
#include <esp_http_server.h>
#include "esp_event.h"
#include "esp_netif.h"
#include "esp_tls.h"
#include <esp_wifi.h>
#include <esp_system.h>
#include "esp_eth.h"
#include "esp_spiffs.h"
#include "driver/gpio.h"

#include "hh_util.h"
#include "sensor_tf_luna.h"
#include "rest_server.h"

#define EXAMPLE_HTTP_QUERY_KEY_MAX_LEN  (64)

/* A simple example that demonstrates how to create GET and POST
 * handlers for the web server.
 */

static const char *TAG = "example";
static tf_luna_ctx_t *g_tf_luna_ctx = NULL;
#define TF_LUNA_INFO_STR_LEN 256

esp_err_t get_tf_dist_handler(httpd_req_t *req)
{
    tf_luna_info_t get_info = {0};
    char tf_luna_info_str[TF_LUNA_INFO_STR_LEN] = {0};
    if (g_tf_luna_ctx) {
        tf_luna_get_info(g_tf_luna_ctx, &get_info);
        snprintf(tf_luna_info_str, sizeof(tf_luna_info_str), "%d", get_info.dist_cm);
    }

    ESP_LOGI(TAG, "resp:\n%s", tf_luna_info_str);
    httpd_resp_send(req, tf_luna_info_str, HTTPD_RESP_USE_STRLEN);
    return ESP_OK;
}

static const httpd_uri_t get_tf_dist = {
    .uri       = "/dist",
    .method    = HTTP_GET,
    .handler   = get_tf_dist_handler,
    .user_ctx  = NULL
};

#if CONFIG_BLINK_LED_GPIO

static void configure_led(void)
{
    ESP_LOGI(TAG, "Example configured to blink GPIO LED!");
    gpio_reset_pin(CONFIG_BLINK_GPIO);
    /* Set the GPIO as a push/pull output */
    gpio_set_direction(CONFIG_BLINK_GPIO, GPIO_MODE_OUTPUT);
}

esp_err_t led_control_handler(httpd_req_t *req)
{
    char*  buf;
    size_t buf_len;

    buf_len = httpd_req_get_url_query_len(req) + 1;
    if (buf_len > 1) {
        buf = malloc(buf_len);
        if(httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) {
            char param[32] = {0};
            if (ESP_OK == httpd_query_key_value(buf, "state", param, sizeof(param))) {
                if (0 == strcmp(param, "on")) {
                    ESP_LOGI(TAG, "led on");
                    gpio_set_level(CONFIG_BLINK_GPIO, 1);
                } else if (0 == strcmp(param, "off")) {
                    ESP_LOGI(TAG, "led off");
                    gpio_set_level(CONFIG_BLINK_GPIO, 0);
                }
            }
        }
        free(buf);
    }
    httpd_resp_send(req, NULL, 0);
    return ESP_OK;
}

static const httpd_uri_t led_control = {
    .uri       = "/control",
    .method    = HTTP_GET,
    .handler   = led_control_handler,
    .user_ctx  = NULL
};

#endif

static rest_server_context_t *start_webserver(void)
{
    rest_server_context_t *p_ctx = NULL;
    rest_server_init_params_t *init_params = NULL;
    uint8_t appended_num = 0;

    init_params = calloc(1, sizeof(rest_server_init_params_t));
    init_params->base_path = CONFIG_EXAMPLE_WEB_MOUNT_POINT;
    init_params->appended_uri[appended_num++] = &get_tf_dist;
#if CONFIG_BLINK_LED_GPIO
    init_params->appended_uri[appended_num++] = &led_control;
#endif

    ESP_ERROR_CHECK(start_rest_server(init_params, &p_ctx));

    free(init_params);
    return p_ctx;
}

static esp_err_t stop_webserver(rest_server_context_t *p_ctx)
{
    // Stop the httpd server
    stop_rest_server(p_ctx);
    return ESP_OK;
}

static void disconnect_handler(void* arg, esp_event_base_t event_base,
                               int32_t event_id, void* event_data)
{
    rest_server_context_t **pp_ctx = (rest_server_context_t **) arg;
    if (*pp_ctx) {
        ESP_LOGI(TAG, "Stopping webserver");
        if (stop_webserver(*pp_ctx) == ESP_OK) {
            *pp_ctx = NULL;
        } else {
            ESP_LOGE(TAG, "Failed to stop http server");
        }
    }
}

static void connect_handler(void* arg, esp_event_base_t event_base,
                            int32_t event_id, void* event_data)
{
    rest_server_context_t **pp_ctx = (rest_server_context_t **) arg;
    if (*pp_ctx == NULL) {
        ESP_LOGI(TAG, "Starting webserver");
        *pp_ctx = start_webserver();
    }
}

esp_err_t init_fs(void)
{
    esp_vfs_spiffs_conf_t conf = {
        .base_path = CONFIG_EXAMPLE_WEB_MOUNT_POINT,
        .partition_label = NULL,
        .max_files = 5,
        .format_if_mount_failed = false
    };
    esp_err_t ret = esp_vfs_spiffs_register(&conf);

    if (ret != ESP_OK) {
        if (ret == ESP_FAIL) {
            ESP_LOGE(TAG, "Failed to mount or format filesystem");
        } else if (ret == ESP_ERR_NOT_FOUND) {
            ESP_LOGE(TAG, "Failed to find SPIFFS partition");
        } else {
            ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret));
        }
        return ESP_FAIL;
    }

    size_t total = 0, used = 0;
    ret = esp_spiffs_info(NULL, &total, &used);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to get SPIFFS partition information (%s)", esp_err_to_name(ret));
    } else {
        ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used);
    }
    return ESP_OK;
}

void app_main(void)
{
    static rest_server_context_t *p_ctx = NULL;

    ESP_ERROR_CHECK(nvs_flash_init());
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    ESP_ERROR_CHECK(init_fs());

#if CONFIG_BLINK_LED_GPIO
    configure_led();
#endif
    ESP_ERROR_CHECK(tf_luna_init(&g_tf_luna_ctx));

    /* This helper function configures Wi-Fi or Ethernet, as selected in menuconfig.
     * Read "Establishing Wi-Fi or Ethernet Connection" section in
     * examples/protocols/README.md for more information about this function.
     */
    ESP_ERROR_CHECK(example_connect());

    /* Register event handlers to stop the server when Wi-Fi or Ethernet is disconnected,
     * and re-start it upon connection.
     */
    ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &connect_handler, &p_ctx));
    ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_STA_DISCONNECTED, &disconnect_handler, &p_ctx));

    /* Start the server for the first time */
    p_ctx = start_webserver();

    while (p_ctx) {
        sleep(5);
    }

    if (g_tf_luna_ctx) {
        tf_luna_deinit(g_tf_luna_ctx);
        g_tf_luna_ctx = NULL;
    }
}
